bitkeeper revision 1.7.3.1 (3df0eb65-VkCkSl4AI6tC2Fnioa_bw)
authorkaf24@labyrinth.cl.cam.ac.uk <kaf24@labyrinth.cl.cam.ac.uk>
Fri, 6 Dec 2002 18:24:37 +0000 (18:24 +0000)
committerkaf24@labyrinth.cl.cam.ac.uk <kaf24@labyrinth.cl.cam.ac.uk>
Fri, 6 Dec 2002 18:24:37 +0000 (18:24 +0000)
hypervisor.h, traps.c, signal.c, process.c, hypervisor-if.h, entry.S:
  First cut at virtualising debug registers

xen-2.4.16/arch/i386/entry.S
xen-2.4.16/arch/i386/traps.c
xen-2.4.16/include/hypervisor-ifs/hypervisor-if.h
xenolinux-2.4.16-sparse/arch/xeno/kernel/process.c
xenolinux-2.4.16-sparse/arch/xeno/kernel/signal.c
xenolinux-2.4.16-sparse/arch/xeno/kernel/traps.c
xenolinux-2.4.16-sparse/include/asm-xeno/hypervisor.h

index 996c1eb82f2d5b696faa67e67f6b8256c6f0a182..ecd878acd1d7dcf12da7558fc0453b7d3a475776 100644 (file)
@@ -302,6 +302,9 @@ FAULT11:movl %eax,4(%esi)
         movl EFLAGS+4(%esp),%eax
 FAULT12:movl %eax,8(%esi)
         /* Rewrite our stack frame and return to ring 1. */
+        /* IA32 Ref. Vol. 3: TF, VM, RF and NT flags are cleared on trap. */
+        andl $0xfffcbeff,%eax
+        movl %eax,EFLAGS+4(%esp)
         movl %ds,OLDSS+4(%esp)
         movl %esi,OLDESP+4(%esp)
         movzwl %es:GTB_CS(%edx),%eax
@@ -521,6 +524,8 @@ ENTRY(hypervisor_call_table)
         .long SYMBOL_NAME(kill_domain)
         .long SYMBOL_NAME(do_dom0_op)
         .long SYMBOL_NAME(do_network_op)
+        .long SYMBOL_NAME(do_set_debugreg)
+        .long SYMBOL_NAME(do_get_debugreg)
         .rept NR_syscalls-(.-hypervisor_call_table)/4
         .long SYMBOL_NAME(sys_ni_syscall)
        .endr
index 6fd88f35f48da2ac598c861d14474326a5fbd849..f0e833dc222e2681d3086019d19f95abb0d90f7b 100644 (file)
@@ -385,53 +385,42 @@ asmlinkage void math_state_restore(struct pt_regs *regs, long error_code)
 }
 
 
-/*
- * Our handling of the processor debug registers is non-trivial.
- * We do not clear them on entry and exit from the kernel. Therefore
- * it is possible to get a watchpoint trap here from inside the kernel.
- * However, the code in ./ptrace.c has ensured that the user can
- * only set watchpoints on userspace addresses. Therefore the in-kernel
- * watchpoint trap can only occur in code which is reading/writing
- * from user space. Such code must not hold kernel locks (since it
- * can equally take a page fault), therefore it is safe to call
- * force_sig_info even though that claims and releases locks.
- * 
- * Code in ./signal.c ensures that the debug control register
- * is restored before we deliver any signal, and therefore that
- * user code runs with the correct debug control register even though
- * we clear it here.
- *
- * Being careful here means that we don't have to be as careful in a
- * lot of more complicated places (task switching can be a bit lazy
- * about restoring all the debug state, and ptrace doesn't have to
- * find every occurrence of the TF bit that could be saved away even
- * by user code)
- */
 asmlinkage void do_debug(struct pt_regs * regs, long error_code)
 {
     unsigned int condition;
     struct task_struct *tsk = current;
+    struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
 
     __asm__ __volatile__("movl %%db6,%0" : "=r" (condition));
 
     /* Mask out spurious debug traps due to lazy DR7 setting */
-    if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) {
-        if (!tsk->thread.debugreg[7])
-            goto clear_dr7;
+    if ( (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) &&
+         (tsk->thread.debugreg[7] == 0) )
+    {
+        __asm__("movl %0,%%db7" : : "r" (0));
+        return;
     }
 
-    /* Save debug status register where ptrace can see it */
-    tsk->thread.debugreg[6] = condition;
+    if ( (regs->xcs & 3) == 0 )
+    {
+        /* Clear TF just for absolute sanity. */
+        regs->eflags &= ~EF_TF;
+        /*
+         * Basically, we ignore watchpoints when they trigger in
+         * the hypervisor. This may happen when a buffer is passed
+         * to us which previously had a watchpoint set on it.
+         * No need to bump EIP; the only faulting trap is an
+         * instruction breakpoint, which can't happen to us.
+         */
+        return;
+    }
 
-    panic("trap up to OS here, pehaps\n");
+    /* Save debug status register where guest OS can peek at it */
+    tsk->thread.debugreg[6] = condition;
 
-    /* Disable additional traps. They'll be re-enabled when
-     * the signal is delivered.
-     */
- clear_dr7:
-    __asm__("movl %0,%%db7"
-            : /* no output */
-            : "r" (0));
+    gtb->flags = GTBF_TRAP_NOCODE;
+    gtb->cs    = tsk->thread.traps[1].cs;
+    gtb->eip   = tsk->thread.traps[1].address;
 }
 
 
@@ -541,18 +530,6 @@ void __init trap_init(void)
      * 
      * 2. All others, we set gate DPL == 0. Any use of "INT n" will thus
      *    cause a GPF with CS:EIP pointing at the faulting instruction.
-     *    We can then peek at the instruction at check if it is of the
-     *    form "0xCD <imm8>". If so, we fake out an exception to the
-     *    guest OS. If the protected read page faults, we patch that up as
-     *    a page fault to the guest OS.
-     *    [NB. Of course we check the "soft DPL" to check that guest OS
-     *     wants to handle a particular 'n'. If not, we pass the GPF up
-     *     to the guest OS untouched.]
-     * 
-     * 3. For efficiency, we may want to allow direct traps by the guest
-     *    OS for certain critical vectors (eg. 0x80 in Linux). These must
-     *    therefore not be mapped by hardware interrupts, and so we'd need
-     *    a static list of them, which we add to on demand.
      */
 
     /* Only ring 1 can access monitor services. */
@@ -594,3 +571,69 @@ long do_fpu_taskswitch(void)
     stts();
     return 0;
 }
+
+
+long do_set_debugreg(int reg, unsigned long value)
+{
+    int i;
+
+    switch ( reg )
+    {
+    case 0: 
+        if ( value > (PAGE_OFFSET-4) ) return -EPERM;
+        __asm__ ( "movl %0, %%db0" : : "r" (value) );
+        break;
+    case 1: 
+        if ( value > (PAGE_OFFSET-4) ) return -EPERM;
+        __asm__ ( "movl %0, %%db1" : : "r" (value) );
+        break;
+    case 2: 
+        if ( value > (PAGE_OFFSET-4) ) return -EPERM;
+        __asm__ ( "movl %0, %%db2" : : "r" (value) );
+        break;
+    case 3:
+        if ( value > (PAGE_OFFSET-4) ) return -EPERM;
+        __asm__ ( "movl %0, %%db3" : : "r" (value) );
+        break;
+    case 6:
+        /*
+         * DR6: Bits 4-11,16-31 reserved (set to 1).
+         *      Bit 12 reserved (set to 0).
+         */
+        value &= 0xffffefff; /* reserved bits => 0 */
+        value |= 0xffff0ff0; /* reserved bits => 1 */
+        __asm__ ( "movl %0, %%db6" : : "r" (value) );
+        break;
+    case 7:
+        /*
+         * DR7: Bit 10 reserved (set to 1).
+         *      Bits 11-12,14-15 reserved (set to 0).
+         * Privileged bits:
+         *      GD (bit 13): must be 0.
+         *      R/Wn (bits 16-17,20-21,24-25,28-29): mustn't be 10.
+         *      LENn (bits 18-19,22-23,26-27,30-31): mustn't be 10.
+         */
+        /* DR7 == 0 => debugging disabled for this domain. */
+        if ( value != 0 )
+        {
+            value &= 0xffff27ff; /* reserved bits => 0 */
+            value |= 0x00000400; /* reserved bits => 1 */
+            if ( (value & (1<<13)) != 0 ) return -EPERM;
+            for ( i = 0; i < 16; i += 2 )
+                if ( ((value >> (i+16)) & 3) == 2 ) return -EPERM;
+        }
+        __asm__ ( "movl %0, %%db7" : : "r" (value) );
+        break;
+    default:
+        return -EINVAL;
+    }
+
+    current->thread.debugreg[reg] = value;
+    return 0;
+}
+
+unsigned long do_get_debugreg(int reg)
+{
+    if ( (reg < 0) || (reg > 7) ) return -EINVAL;
+    return current->thread.debugreg[reg];
+}
index 2491557325990814aea4b9a058d39f58dc1063b9..4f4d1a2d235012b6c0c81a11d46526511051fccc 100644 (file)
@@ -40,6 +40,8 @@ typedef struct
 #define __HYPERVISOR_exit            8
 #define __HYPERVISOR_dom0_op         9
 #define __HYPERVISOR_network_op     10
+#define __HYPERVISOR_set_debugreg   11
+#define __HYPERVISOR_get_debugreg   12
 
 #define TRAP_INSTR "int $0x82"
 
index e4c236bf98f56ee9acda798b7f95da7101eda801..9e26f3d65aca7ac8880e37b0d7fee54eee447ae2 100644 (file)
@@ -187,6 +187,7 @@ void flush_thread(void)
     struct task_struct *tsk = current;
 
     memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8);
+
     /*
      * Forget coprocessor state..
      */
@@ -306,14 +307,6 @@ void dump_thread(struct pt_regs * regs, struct user * dump)
     dump->u_fpvalid = dump_fpu (regs, &dump->i387);
 }
 
-/*
- * This special macro can be used to load a debugging register
- */
-#define loaddebug(thread,register) \
-               __asm__("movl %0,%%db" #register  \
-                       : /* no output */ \
-                       :"r" (thread->debugreg[register]))
-
 /*
  *     switch_to(x,yn) should switch tasks from x to y.
  *
@@ -359,20 +352,19 @@ void __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
     loadsegment(fs, next->fs);
     loadsegment(gs, next->gs);
 
-#if 0
     /*
      * Now maybe reload the debug registers
      */
-    if (next->debugreg[7]){
-        loaddebug(next, 0);
-        loaddebug(next, 1);
-        loaddebug(next, 2);
-        loaddebug(next, 3);
+    if ( next->debugreg[7] != 0 )
+    {
+        HYPERVISOR_set_debugreg(0, next->debugreg[0]);
+        HYPERVISOR_set_debugreg(1, next->debugreg[1]);
+        HYPERVISOR_set_debugreg(2, next->debugreg[2]);
+        HYPERVISOR_set_debugreg(3, next->debugreg[3]);
         /* no 4 and 5 */
-        loaddebug(next, 6);
-        loaddebug(next, 7);
+        HYPERVISOR_set_debugreg(6, next->debugreg[6]);
+        HYPERVISOR_set_debugreg(7, next->debugreg[7]);
     }
-#endif
 }
 
 asmlinkage int sys_fork(struct pt_regs regs)
index c0f43e63426c8cecaa0cbbcfcd98a4d151ee53d9..d4244d095ffea34568b2944b0c7f35f29457b1aa 100644 (file)
@@ -693,6 +693,13 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset)
                        }
                }
 
+                /* Reenable any watchpoints before delivering the
+                 * signal to user space. The processor register will
+                 * have been cleared if the watchpoint triggered
+                 * inside the kernel.
+                 */
+                HYPERVISOR_set_debugreg(7, current->thread.debugreg[7]);
+
                /* Whee!  Actually deliver the signal.  */
                handle_signal(signr, ka, &info, oldset, regs);
                return 1;
index 6e8f347420ef1a7c55122da857875a39d9cd613d..2a546b49d54d85c713c37698392883c914fb3087 100644 (file)
@@ -324,11 +324,60 @@ gp_in_kernel:
 
 asmlinkage void do_debug(struct pt_regs * regs, long error_code)
 {
-    /*
-     * We don't mess with breakpoints, so the only way this exception
-     * type can occur is through single-step mode.
+    unsigned int condition;
+    struct task_struct *tsk = current;
+    siginfo_t info;
+
+    condition = HYPERVISOR_get_debugreg(6);
+
+    /* Mask out spurious debug traps due to lazy DR7 setting */
+    if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) {
+        if (!tsk->thread.debugreg[7])
+            goto clear_dr7;
+    }
+
+    /* Save debug status register where ptrace can see it */
+    tsk->thread.debugreg[6] = condition;
+
+    /* Mask out spurious TF errors due to lazy TF clearing */
+    if (condition & DR_STEP) {
+        /*
+         * The TF error should be masked out only if the current
+         * process is not traced and if the TRAP flag has been set
+         * previously by a tracing process (condition detected by
+         * the PT_DTRACE flag); remember that the i386 TRAP flag
+         * can be modified by the process itself in user mode,
+         * allowing programs to debug themselves without the ptrace()
+         * interface.
+         */
+        if ((tsk->ptrace & (PT_DTRACE|PT_PTRACED)) == PT_DTRACE)
+            goto clear_TF;
+    }
+
+    /* Ok, finally something we can handle */
+    tsk->thread.trap_no = 1;
+    tsk->thread.error_code = error_code;
+    info.si_signo = SIGTRAP;
+    info.si_errno = 0;
+    info.si_code = TRAP_BRKPT;
+        
+    /* If this is a kernel mode trap, save the user PC on entry to 
+     * the kernel, that's what the debugger can make sense of.
      */
+    info.si_addr = ((regs->xcs & 3) == 0) ? (void *)tsk->thread.eip : 
+                                            (void *)regs->eip;
+    force_sig_info(SIGTRAP, &info, tsk);
+
+    /* Disable additional traps. They'll be re-enabled when
+     * the signal is delivered.
+     */
+ clear_dr7:
+    HYPERVISOR_set_debugreg(7, 0);
+    return;
+
+ clear_TF:
     regs->eflags &= ~TF_MASK;
+    return;
 }
 
 
index 522f124e86336bb2c7b3ba5d55fdd370ec7bc983..f46bfe4c449178b6ea25f67ee7369ef7df4b4681 100644 (file)
@@ -163,4 +163,26 @@ static inline int HYPERVISOR_network_op(void *network_op)
     return ret;
 }
 
+static inline int HYPERVISOR_set_debugreg(int reg, unsigned long value)
+{
+    int ret;
+    __asm__ __volatile__ (
+        TRAP_INSTR
+        : "=a" (ret) : "0" (__HYPERVISOR_set_debugreg),
+        "b" (reg), "c" (value) );
+
+    return ret;
+}
+
+static inline unsigned long HYPERVISOR_get_debugreg(int reg)
+{
+    unsigned long ret;
+    __asm__ __volatile__ (
+        TRAP_INSTR
+        : "=a" (ret) : "0" (__HYPERVISOR_get_debugreg),
+        "b" (reg) );
+
+    return ret;
+}
+
 #endif /* __HYPERVISOR_H__ */